package kandl.automation.testsuite.excel;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.runners.model.InitializationError;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import kandl.automation.Browser;
import kandl.automation.testsuite.TestCase;
import kandl.automation.testsuite.TestProfile;
import kandl.automation.testsuite.TestStep;
import kandl.automation.testsuite.TestSuite;

public class ExcelTestSuite implements TestSuite {

    private static final String SUITE_ID_LABEL = "Test Rail Suite ID";
    private static final String PLAN_SHEET_NAME = "SupportedOS-Browsers";
    private static final String PLAN_ID_LABEL = "Test Rail Plan ID";
    private static final String ALL_BROWSERS = "All";
    private static final int HEADER_ROWS_COUNT = 1;

    private String id;
    private String name;
    private String planId;

    private List<TestProfile> profiles;
    private List<TestCase> cases;
    private List<TestStep> steps;

    public ExcelTestSuite(String name) throws InitializationError {
        this.name = name;
        InputStream myStream = getClass().getClassLoader().getResourceAsStream(name + ".xls");
        if (myStream == null) {
            throw new InitializationError("No source found for tests: Cannot find " + name + " on classpath");
        }
        try {
            Workbook workbook = new HSSFWorkbook(myStream);
            load(workbook);
        }
        catch (IOException e) {
            throw new InitializationError(e);
        }
    }

    public ExcelTestSuite(Workbook workbook) throws InitializationError {
        load(workbook);
    }

    @Override
    public List<TestProfile> getProfiles() {
        return profiles;
    }

    @Override
    public List<TestCase> getTestCases(TestProfile profile) {
        List<TestCase> matches = new ArrayList<TestCase>();
        for (TestCase tc : cases) {
            if (tc.getBrowsers().equals(ALL_BROWSERS)
             || tc.getBrowsers().contains(profile.getId())) {
                matches.add(tc);
            }
        }
        return matches;
    }

    @Override
    public List<TestStep> getTestSteps(TestCase testCase) {
        List<TestStep> matches = new ArrayList<TestStep>();
        for (TestStep step : steps) {
            if (step.getTestCaseId().equals(testCase.getId())) {
                matches.add(step);
            }
        }
        return matches;
    }

    public String toString() {
        return name;
    }

    private void load(Workbook workbook) throws InitializationError {

        this.id = getSuiteId(workbook);
        this.planId = getPlanId(workbook);

        this.profiles = readProfiles(workbook);
        this.cases = readTestCases(workbook);
        this.steps = readTestSteps(workbook);
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public String getPlanId() {
        return planId;
    }

    @Override
    public String getName() {
        return name;
    }

    private String getPlanId(Workbook workbook) throws InitializationError {
        return getFieldFromPlanSheet(workbook, PLAN_ID_LABEL);
    }

    private String getSuiteId(Workbook workbook) throws InitializationError {
        return getFieldFromPlanSheet(workbook, SUITE_ID_LABEL);
    }

    private String getFieldFromPlanSheet(Workbook workbook, String fieldLabel) throws InitializationError {
        Sheet sheet = workbook.getSheet(PLAN_SHEET_NAME);
        if (sheet == null) {
            throw new InitializationError("Cannot find sheet " + PLAN_SHEET_NAME);
        }
        for(int i=0; i<=sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);
            if (row.getCell(0).getCellType() == Cell.CELL_TYPE_STRING
                && row.getCell(0).getStringCellValue().equals(fieldLabel)) {
                return row.getCell(1).getStringCellValue();
            }
        }
        throw new InitializationError(fieldLabel + " is not found for Sheet name: " + PLAN_SHEET_NAME);
    }

    private List<TestProfile> readProfiles(Workbook workbook) throws InitializationError {
        List<TestProfile> list = new ArrayList<TestProfile>();
        String[][] testDataCell = readData(HEADER_ROWS_COUNT, "SupportedOS-Browsers", workbook);
        for(int i = 2; i < testDataCell.length; i++) {
            String id = checkEmptyTestField(testDataCell[i][0], "Test Data ID Field - Row:", i);
            boolean enabled = checkEmptyTestField(testDataCell[i][1], "Test Data Execute - Row:", i).equals("Y");
            String url = checkEmptyTestField(testDataCell[i][2], "Test Data URL - Row:", i);
            String browserType = checkEmptyTestField(testDataCell[i][3], "Test Data Browser Type - Row:", i);
            String browserVersion = checkEmptyTestField(testDataCell[i][4], "Test Data Browser Version - Row:", i);
            String platform = checkEmptyTestField(testDataCell[i][5], "Test Data OS- Platform - Row:", i);
            String testRailConfig = checkEmptyTestField(testDataCell[i][6], "Test Data Tesrail config - Row:", i);

            list.add(new TestProfile(id, url, new Browser(browserType, testRailConfig), browserVersion, platform, enabled));
        }
        return list;
    }

    private List<TestCase> readTestCases(Workbook workbook) throws InitializationError {

        List<TestCase> list = new ArrayList<TestCase>();
        String[][] testCaseCell = readData(HEADER_ROWS_COUNT, "TestCases", workbook);

        for(int i = 0; i < testCaseCell.length; i ++) {
            String id = checkEmptyTestField(testCaseCell[i][1], "Test Case ID Field - Row:", i);
            String testRailId = checkEmptyTestField(testCaseCell[i][2], "Test Case TestRail Id - Row:", i);
            String desc = checkEmptyTestField(testCaseCell[i][3], "Test Case Description Field - Row:", i);
            boolean enabled = checkEmptyTestField(testCaseCell[i][4], "Test Case Execute Field - Row:", i).equals("Y");
            String browsers = checkEmptyTestField(testCaseCell[i][7], "Test Case Browsers Field - Row:", i);
            list.add(new TestCase(id, testRailId, desc, enabled, browsers));
        }
        return list;
    }

    public List<TestStep> readTestSteps(Workbook workbook) throws InitializationError {

        List<TestStep> list = new ArrayList<TestStep>();
        String[][] testStepCell = readData(HEADER_ROWS_COUNT, "TestSteps", workbook);

        for(int i = 0; i < testStepCell.length; i ++) {
            String tcID = checkEmptyTestField(testStepCell[i][0], "Test Step Case ID Field - Row: ", i);
            String id = checkEmptyTestField(testStepCell[i][2], "Test Step ID Field - Row:", i);
            String keyword = checkEmptyTestField(testStepCell[i][4], "Test Step Keyword Field - Row:", i);
            String ip1 = checkEmptyTestField(testStepCell[i][5], "Test Step IP1 Field - Row:", i);
            String ip2 = checkEmptyTestField(testStepCell[i][6], "Test Step IP2 Field - Row:", i);
            String ip3 = checkEmptyTestField(testStepCell[i][7], "Test Step IP3 Field - Row:", i);

            list.add(new TestStep(id, tcID, keyword, ip1, ip2, ip3));
        }
        return list;
    }

    private String[][] readData(int startRow, String sheetName, Workbook workbook) throws InitializationError {
        Sheet mySheet = workbook.getSheet(sheetName);
        if(mySheet == null) {
            throw new InitializationError("Sheet name " + sheetName + " not found.");
        }

        int rowCount = mySheet.getLastRowNum()+1;
        int colCount = mySheet.getRow(0).getLastCellNum();

        String[][] data = new String[rowCount-startRow][colCount];
        for (int i=startRow, target=0; i < rowCount; i++, target++) {
            Row row = mySheet.getRow(i);
            for (int j = 0; j < colCount; j++) {
                Cell cell = row.getCell(j);
                String value = cellToString(cell);
                data[target][j] = value;
            }
        }
        return data;
    }

    private String checkEmptyTestField(String str, String desc, int i) throws InitializationError {
        String param = str;
        if(null == param || param.trim().isEmpty()) {
            throw new InitializationError(desc + i);
        }
        return param;
    }


    public static String cellToString(Cell cell) {
        cell.setCellType(Cell.CELL_TYPE_STRING);
        return cell.getStringCellValue();
    }
}
